package com.sneakxy.cloudbase.platform.controller;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.domain.Sort.Order;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import com.sneakxy.cloudbase.platform.bean.Account;
import com.sneakxy.cloudbase.platform.bean.AccountRole;
import com.sneakxy.cloudbase.platform.bean.AccountRoleId;
import com.sneakxy.cloudbase.platform.bean.Role;
import com.sneakxy.cloudbase.platform.bean.ValidAdd;
import com.sneakxy.cloudbase.platform.bean.ValidEdit;
import com.sneakxy.cloudbase.platform.bean.dto.AccountDTO;
import com.sneakxy.cloudbase.platform.bean.dto.RoleDTO;
import com.sneakxy.cloudbase.platform.bean.dto.RoleDataDTO;
import com.sneakxy.cloudbase.platform.service.IAccountRoleService;
import com.sneakxy.cloudbase.platform.service.IAccountService;
import com.sneakxy.cloudbase.platform.service.IRoleService;
import com.sneakxy.cloudbase.platform.service.ISettingService;
import com.sneakxy.cloudbase.platform.service.ShiroRealm;
import com.sneakxy.cloudbase.platform.utils.web.BeanHelper;
import com.sneakxy.cloudbase.platform.utils.web.DataGrid;
import com.sneakxy.cloudbase.platform.utils.web.PageSearch;
import com.sneakxy.cloudbase.platform.utils.web.Response;
import com.sneakxy.cloudbase.platform.utils.web.signature.RequireSignature;
import com.sneakxy.mybatis.commons.utils.MybatisUtils;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import springfox.documentation.annotations.ApiIgnore;

@Api(tags="账户服务接口")
@RestController
@RequestMapping("/api/account/")
public class AccountController extends BaseController {

	@Autowired
	private IRoleService roleService;
	
	@Autowired
	private IAccountService accountService;
	
	@Autowired
	private IAccountRoleService accountRoleService;
	
	@Autowired
	private PasswordEncoder passwordEncoder;
	
	@Autowired
	private ISettingService settingService;
	
	@Autowired
	private ShiroRealm shiroRealm;
	
	@ApiOperation("账户查询")
	@RequireSignature
	@RequiresPermissions("sys:account:manager")
	@PostMapping("datagrid")
	public Response<DataGrid<AccountDTO>> datagrid(PageSearch<String> search) {
		if(StringUtils.isBlank(search.getSortName())) {
			search.setSortName("createtime");
		}
		Page<Account> page = this.accountService.findBySearch(search.getSearch(), search.toPageable());
		//安全考虑，去除密码
		for(Account account:page.getContent()) {
			account.setPassword(null);
		}
		return Response.ok(DataGrid.newInstance(page, AccountDTO::from));
	}
	
	@ApiImplicitParams({
		@ApiImplicitParam(name="account", value="账户", dataType="String", dataTypeClass = String.class, paramType="query", required=true),
		@ApiImplicitParam(name="host", value="作用域", dataType="String", dataTypeClass = String.class, paramType="query", required=false),
		@ApiImplicitParam(name="displayName", value="显示名，用于页面显示使用", dataType="String", dataTypeClass = String.class, paramType="query", required=true),
		@ApiImplicitParam(name="password", value="密码", dataType="String", dataTypeClass = String.class, paramType="query", required=true),
		@ApiImplicitParam(name="status", value="账号状态", dataType="int", dataTypeClass = Integer.class, paramType="query", required=true),
		@ApiImplicitParam(name="memo", value="备注", dataType="String", dataTypeClass = String.class, paramType="query", required=false)
	})
	@ApiOperation("添加账户")
	@RequireSignature
	@RequiresPermissions("sys:account:manager:add")
	@PostMapping("add")
	public Response<AccountDTO> add(
			@Validated(ValidAdd.class)
			@ApiIgnore 
			AccountDTO dto) {
		BeanHelper.trimToNull(dto);
		
		if(!accountService.validate(dto.getAccount(), dto.getHost())) {
			return Response.fail(1);
		}
		Account account = AccountDTO.to(dto);
		account.setId(MybatisUtils.getUUID());
		account.setNew(true);
		account.setPassword(this.passwordEncoder.encode(account.getPassword()));
		
		account = accountService.save(account);
		//安全考虑，去除密码
		account.setPassword(null);
		return Response.ok(AccountDTO.from(account));
	}
	
	@ApiImplicitParams({
		@ApiImplicitParam(name="id", value="账户ID", dataType="String", dataTypeClass = String.class, paramType="query", required=true),
		@ApiImplicitParam(name="account", value="账户", dataType="String", dataTypeClass = String.class, paramType="query", required=true),
		@ApiImplicitParam(name="host", value="作用域", dataType="String", dataTypeClass = String.class, paramType="query", required=false),
		@ApiImplicitParam(name="displayName", value="显示名，用于页面显示使用", dataType="String", dataTypeClass = String.class, paramType="query", required=true),
		@ApiImplicitParam(name="password", value="密码", dataType="String", dataTypeClass = String.class, paramType="query", required=false),
		@ApiImplicitParam(name="status", value="账号状态", dataType="int", dataTypeClass = Integer.class, paramType="query", required=true),
		@ApiImplicitParam(name="memo", value="备注", dataType="String", dataTypeClass = String.class, paramType="query", required=false)
	})
	@ApiOperation("账户更新")
	@RequireSignature
	@RequiresPermissions("sys:account:manager:edit")
	@PostMapping("edit")
	public Response<AccountDTO> edit(
			@Validated(ValidEdit.class) 
			@ApiIgnore 
			AccountDTO dto) {
		BeanHelper.trimToEmpty(dto);
		
		if(!accountService.validate(dto.getId(), dto.getAccount(), dto.getHost())) {
			return Response.fail(1);
		}
		
		boolean needClearCache = false;
		Account old = accountService.findOne(dto.getId());
		needClearCache = !StringUtils.equals(StringUtils.trimToEmpty(old.getHost()), dto.getHost());
		Account account = AccountDTO.to(dto);
		if(StringUtils.isNotBlank(account.getPassword())) {
			account.setPassword(this.passwordEncoder.encode(account.getPassword()));
			needClearCache = true;
		} else {
			account.setPassword(null);
		}
		
		if(needClearCache) {
			//删除旧的认证缓存
			shiroRealm.clearCachedAuthenticationInfo(old.getAccount(), old.getHost());
		}
		
		account.setNew(false);
		account.setCreatetime(null);
		account.setUpdatetime(null);
		account = accountService.save(account);
		//安全考虑，去除密码
		account.setPassword(null);
		return Response.ok(AccountDTO.from(account));
	}
	
	@ApiOperation("账户验证")
	@RequireSignature
	@RequiresPermissions("sys:account:manager:add")
	@PostMapping("validateAccountAdd")
	public Response<Boolean> validate(
			@ApiParam(name="account", value="账户", required=true)
			@RequestParam(name="account")
			String account,
			@RequestParam(name="host", required = false)
			@ApiParam(name="host", value="域名", required=false)
			String host) {
		return Response.ok(accountService.validate(account, host));
	}
	
	@ApiOperation("账户验证")
	@RequireSignature
	@RequiresPermissions("sys:account:manager:edit")
	@PostMapping("validateAccountEdit")
	public Response<Boolean> validate(
			@ApiParam(name="id", value="账户ID", required=true)
			@RequestParam(name="id")
			String id, 
			@ApiParam(name="account", value="账户", required=true)
			@RequestParam(name="account")
			String account,
			@RequestParam(name="host", required = false)
			@ApiParam(name="host", value="域名", required=false)
			String host) {
		return Response.ok(accountService.validate(id, account, host));
	}
	
	@ApiOperation("删除账户")
	@RequireSignature
	@RequiresPermissions("sys:account:manager:remove")
	@PostMapping("remove")
	public Response<String> remove(
			@ApiParam(name="ids", value="账户ID集合", required=true)
			@RequestParam(name="ids")
			String[] ids) {
		if(ids != null) {
			for(String id:ids) {
				accountService.removeCascad(id);
			}
		}
		return Response.ok();
	}
	
	@ApiOperation("账户角色信息")
	@RequireSignature
	@RequiresPermissions("sys:account:manager:role")
	@PostMapping("roleData")
	public Response<RoleDataDTO> roleData(
			@ApiParam(name="id", value="账户ID", required=true)
			@RequestParam(name="id")
			String id) {
		List<String> selectors = accountRoleService.findRoles(id);
		Iterable<Role> roles = roleService.findAll(Sort.by(new Order(Direction.ASC, "sort")));
		RoleDataDTO result  = new RoleDataDTO();
		result.setRoles(RoleDTO.from(roles));
		result.setSelectors(selectors);
		return Response.ok(result);
	}
	
	@ApiOperation("账户分配角色")
	@RequireSignature
	@RequiresPermissions("sys:account:manager:role")
	@PostMapping("role")
	public Response<String> role(
			@ApiParam(name="id", value="账户ID", required=true)
			@RequestParam(name="id")
			String id,
			@ApiParam(name="roleIds", value="角色ID集合")
			@RequestParam(name="roleIds", required=false)
			String[] roleIds) {
		accountRoleService.deleteByAccountId(id);
		if(roleIds != null) {
			List<AccountRole> entities = new ArrayList<>();
			for(String roleId: roleIds) {
				entities.add(new AccountRole(id, roleId, true));
			}
			this.accountRoleService.saveSplitTable(entities);
		}
		return Response.ok();
	}
	
	@ApiOperation("账户分配角色（不会删除原有角色）")
	@RequireSignature
	@PostMapping("roles")
	public Response<String> roles(
			@ApiParam(name="id", value="账户ID", required=true)
			@RequestParam(name="id")
			String id,
			@ApiParam(name="roleIds", value="角色ID集合", required=true)
			@RequestParam(name="roleIds")
			String[] roleIds) {
		if(roleIds != null) {
			String tableName = accountRoleService.getTableName(id);
			AccountRole relation = null;
			for(String roleId: roleIds) {
				relation = new AccountRole(id, roleId, true);
				if(accountRoleService.exists(relation.getId(), tableName)) {
					continue;
				}
				accountRoleService.save(relation, tableName);
			}
		}
		return Response.ok();
	}
	
	@ApiOperation("删除账户已分配的角色")
	@RequireSignature
	@PostMapping("rmroles")
	public Response<String> rmroles(
			@ApiParam(name="id", value="账户ID", required=true)
			@RequestParam(name="id")
			String id,
			@ApiParam(name="roleIds", value="角色ID集合", required=true)
			@RequestParam(name="roleIds")
			String[] roleIds) {
		if(roleIds != null) {
			String tableName = accountRoleService.getTableName(id);
			for(String roleId: roleIds) {
				accountRoleService.delete(new AccountRoleId(id, roleId), tableName);
			}
		}
		return Response.ok();
	}
	
	@ApiOperation("内部服务使用账户查询")
	@RequireSignature
	@RequiresPermissions("sys:account:manager:view")
	@PostMapping("one")
	public Response<AccountDTO> one(
			@ApiParam(name="accountId", value="账户ID", required=true)
			@RequestParam(name="accountId")
			String accountId) {
		Account account = accountService.findOne(accountId);
		if(account != null) {
			//安全考虑，去除密码
			account.setPassword(null);
		}
		return Response.ok(account, AccountDTO::from);
	}
	
	@ApiOperation("内部服务使用登录账户查询")
	@RequireSignature
	@PostMapping("account")
	public Response<AccountDTO> account(
			@ApiParam(name="principal", value="认证信息", required=true)
			@RequestParam(name="principal")
			String principal,
			@RequestParam(name="host", required = false)
			@ApiParam(name="host", value="域名", required=false)
			String host) {
		Account account = accountService.findOneByAccount(principal, host);
		return Response.ok(AccountDTO.from(account));
	}
	
	@ApiOperation("内部服务使用账户集合查询")
	@RequireSignature
	@RequiresPermissions("sys:account:manager:view")
	@PostMapping("multiple")
	public Response<AccountDTO[]> multiple(
			@ApiParam(name="accountIds", value="账户ID集合", required=true)
			@RequestParam(name="accountIds")
			String[] accountIds) {
		AccountDTO[] accounts = new AccountDTO[accountIds.length];
		Account account = null;
		for(int i=0; i<accountIds.length; i++) {
			account =  accountService.findOne(accountIds[i]);
			if(account != null) {
				//安全考虑，去除密码
				account.setPassword(null);
				accounts[i] = AccountDTO.from(account);
			}
		}
		return Response.ok(accounts);
	}
	
	@ApiOperation("内部服务使用根据角色ID账户查询")
	@RequireSignature
	@RequiresPermissions("sys:account:manager:view")
	@PostMapping("datagridByRoleId")
	public Response<DataGrid<AccountDTO>> datagrid(
			PageSearch<String> search,
			@ApiParam(name="roleId", value="角色ID", required=true)
			@RequestParam(name="roleId")
			String roleId) {
		if(StringUtils.isBlank(search.getSortName())) {
			search.setSortName("createtime");
		}
		Page<Account> page = this.accountService.findBySearch(roleId, search.getSearch(), search.toPageable());
		//安全考虑，去除密码
		for(Account account:page.getContent()) {
			account.setPassword(null);
		}
		return Response.ok(DataGrid.newInstance(page, AccountDTO::from));
	}
	
	@ApiOperation("用户所有设置")
	@RequireSignature
	@GetMapping("settings")
	public Response<Map<String, String>> settings(
				@ApiParam(name="accountId", value="账号ID", required=true)
				@RequestParam(name="accountId")
				String accountId) {
		Map<String, String> settings = settingService.findSettings(accountId);
		return Response.ok(settings);
	}
	
	@ApiOperation("用户单个设置")
	@RequireSignature
	@GetMapping("single/setting")
	public Response<String> setting(
				@ApiParam(name="accountId", value="账号ID", required=true)
				@RequestParam(name="accountId")
				String accountId,
				@ApiParam(name="key", value="键", required=true)
				@RequestParam(name="key")
				String key) {
		String value = settingService.findValueByKey(accountId, key);
		return Response.ok(value);
	}
	
	@ApiOperation("用户变量设置")
	@RequireSignature
	@PostMapping("setting")
	public Response<Boolean> setting(
			@ApiParam(name="accountId", value="账号ID", required=true)
			@RequestParam(name="accountId")
			String accountId,
			@ApiParam(name="key", value="键", required=true)
			@RequestParam(name="key")
			String key,
			@ApiParam(name="value", value="键值")
			@RequestParam(name="value", required=false)
			String value) {
		if(StringUtils.isBlank(value)) {
			settingService.deleteByKey(accountId, key);
		} else {
			settingService.save(accountId, key, value);
		}
		return Response.ok(true);
	}
	
}
